#include <linux/module.h> /* 模块初始化、卸载的接口头文件  */
#include <linux/cdev.h> /* 字符设备头文件 */
#include <linux/fs.h>
#include <linux/device.h> /* class和device依赖 的头文件 */


#define DEVICE_NAME "aml_char"
#define CLASS_NAME DEVICE_NAME

static struct cdev aml_cdev;
static dev_t dev_num;
static struct class*  amlClass;



/* 应用层系统调用：open()、fopen 打开设备节点文件时，将回调此函数  */
static int aml_cdev_open(struct inode *inode, struct file *file)
{
	pr_info("aml_cdev_open() is called.\n");
	return 0;
}

/* 应用层系统调用：close()、fclose 关闭设备节点文件时，将回调此函数  */
static int aml_cdev_close(struct inode *inode, struct file *file)
{
	pr_info("aml_cdev_close() is called.\n");
	return 0;
}

/*  应用层系统调用：ioctrl() 操作设备节点文件时，将回调此函数  */
static long aml_cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	pr_info("aml_cdev_ioctl() is called. cmd = %d, arg = %ld\n", cmd, arg);
	return 0;
}

/* 注册回调函数：只需要实现需要的接口即可 */
static const struct file_operations aml_cdev_fops = {
	.owner = THIS_MODULE,
	.open = aml_cdev_open,
	.release = aml_cdev_close,
	.unlocked_ioctl = aml_cdev_ioctl,
};

/* 模块初始化函数  */
static int __init aml_cdev_init(void)
{
	int ret;
	dev_t dev_no;
	int major, minor;

	struct device* amlDevice;
	
	pr_info("aml_cdev_init \n");

	/* 让系统自动分配一个设备号，同时再注册并获取此设备号 */
	ret = alloc_chrdev_region(&dev_no, 0, 1, DEVICE_NAME);
	if (ret < 0){
		pr_info("Failed: alloc_chrdev_region \n");
		return ret;
	}
		
	major = MAJOR(dev_no);
	minor = MINOR(dev_no);
	dev_num = MKDEV(major,0);
	pr_info("alloc major(%d), minor(%d)\n", major, minor);
	

	/* 初始化aml_cdev，并将其添加内核中*/
	cdev_init(&aml_cdev, &aml_cdev_fops);
	ret= cdev_add(&aml_cdev, dev_num, 1);
	if (ret < 0){
		unregister_chrdev_region(dev_num, 1);
		pr_info("Failed: add cdev\n");
		return ret;
	}

	/* 注册 device class */
	amlClass = class_create(THIS_MODULE, CLASS_NAME);
	if (IS_ERR(amlClass)){
		unregister_chrdev_region(dev_num, 1);
		cdev_del(&aml_cdev);
	    pr_info("Failed: register device class\n");
	    return PTR_ERR(amlClass);
	}
	pr_info("/sys/class/%s auto created!\n", CLASS_NAME);

	/* 创建设备节点文件：  /dev/your-dev-name */
	amlDevice = device_create(amlClass, NULL, dev_num, NULL, DEVICE_NAME);
	if (IS_ERR(amlDevice)){
	    class_destroy(amlClass);
	    cdev_del(&aml_cdev);
	    unregister_chrdev_region(dev_num, 1);
	    pr_info("Failed to create the device\n");
	    return PTR_ERR(amlDevice);
	}
	pr_info("/dev/%s auto created!\n", CLASS_NAME);

	return 0;
}

/* 模块退出时执行卸载操作  */
static void __exit aml_cdev_exit(void)
{
	pr_info("aml_cdev_exit\n");
	device_destroy(amlClass, dev_num); // 移除 /dev/aml_char
	class_destroy(amlClass);           // 移除   /sys/class/aml_char
	cdev_del(&aml_cdev);//删除内核cdev设备
	unregister_chrdev_region(dev_num, 1); //注销设备号
}


/* 固定的模板部分，将导出模块的初始化和卸载接口符号 */
module_init(aml_cdev_init);
module_exit(aml_cdev_exit);

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("szhou <66176468@qq.com>");
MODULE_DESCRIPTION("This is a amlogic debug tool.");
